package com.zhugang.week03;

import java.util.Stack;

/**
 * @program algorithms
 * @description: stackCalculate: 求表达式值 ： 5 + 3 * 2 + 1 - 2 * 3 - 4
 * @author: chanzhugang
 * @create: 2022/06/03 23:12
 */
public class StackCalculate {

    public static void main(String[] args) {
        StackCalculate stackCalculate = new StackCalculate();
        String s = "5 + 3 * 2 + 1 - 2 * 3 - 4";
        int res = stackCalculate.calculate(s);
        System.out.println(res);
    }

    public int calculate(String s) {
        /**
         * 计算器/表达式求值
         */
        // 数字直接压入数字栈；运算符栈为空或者运算符c > 栈顶元素，c直接压入运算符栈；运算符c < 栈顶运算符，栈顶出栈计算
        Stack<Integer> nums = new Stack<>();
        Stack<Character> ops = new Stack<>();
        int i = 0;
        int n = s.length();
        while (i < n) {
            char c = s.charAt(i);
            if (c == ' ') {
                i++;
            } else if (isDigit(c)) {
                int number = 0;
                while (i < n && isDigit(s.charAt(i))) {
                    // 多个数字字符转成数字
                    number = number * 10 + (s.charAt(i) - '0');
                    i++;
                }
                nums.push(number);
            } else {
                if (ops.isEmpty() || prior(c, ops.peek())) {
                    ops.push(c);
                } else {
                    while (!ops.isEmpty() && !prior(c, ops.peek())) {
                        // 取栈顶元素计算
                        fetchAndCal(nums, ops);
                    }
                    ops.push(c);
                }
                i++;
            }
        }
        // 字符栈还有数据
        while (!ops.isEmpty()) {
            fetchAndCal(nums, ops);
        }
        return nums.pop();
    }

    private void fetchAndCal(Stack<Integer> nums, Stack<Character> ops) {
        Integer num2 = nums.pop();
        Integer num1 = nums.pop();
        Character op = ops.pop();
        int res = cal(op, num1, num2);
        // 计算后的结果压入数字栈
        nums.push(res);
    }

    private int cal(Character op, Integer num1, Integer num2) {
        switch (op) {
            case '+':
                return num1 + num2;
            case '-':
                return num1 - num2;
            case '*':
                return num1 * num2;
            case '/':
                return num1 / num2;
        }
        return -1;

    }

    private boolean prior(char a, Character b) {
        return (a == '*' || a == '/') && (b == '+' || b == '-');
    }

    private boolean isDigit(char c) {
        return c >= '0' && c <= '9';
    }

}